/**
 * Development repository: https://github.com/kaisalmen/WWOBJLoader
 */

const ObjectManipulator = function () {
};

ObjectManipulator.prototype = {

  constructor: ObjectManipulator,

  /**
   * Applies values from parameter object via set functions or via direct assignment.
   *
   * @param {Object} objToAlter The objToAlter instance
   * @param {Object} params The parameter object
   * @param {boolean} forceCreation Force the creation of a property
   */
  applyProperties: function (objToAlter, params, forceCreation) {

    // fast-fail
    if (objToAlter === undefined || objToAlter === null || params === undefined || params === null) return;

    let property, funcName, values;
    for (property in params) {

      funcName = 'set' + property.substring(0, 1).toLocaleUpperCase() + property.substring(1);
      values = params[property];

      if (typeof objToAlter[funcName] === 'function') {

        objToAlter[funcName](values);

      } else if (objToAlter.hasOwnProperty(property) || forceCreation) {

        objToAlter[property] = values;

      }

    }

  }
};

const DefaultWorkerPayloadHandler = function (parser) {

  this.parser = parser;
  this.logging = {
    enabled: false,
    debug: false
  };

};

DefaultWorkerPayloadHandler.prototype = {

  constructor: DefaultWorkerPayloadHandler,

  handlePayload: function (payload) {

    if (payload.logging) {

      this.logging.enabled = payload.logging.enabled === true;
      this.logging.debug = payload.logging.debug === true;

    }

    if (payload.cmd === 'parse') {

      const scope = this;
      const callbacks = {
        callbackOnAssetAvailable: function (payload) {

          self.postMessage(payload);

        },
        callbackOnProgress: function (text) {

          if (scope.logging.enabled && scope.logging.debug) console.debug('WorkerRunner: progress: ' + text);

        }
      };

      const parser = this.parser;
      if (typeof parser['setLogging'] === 'function') {

        parser.setLogging(this.logging.enabled, this.logging.debug);

      }

      const objectManipulator = new ObjectManipulator();
      objectManipulator.applyProperties(parser, payload.params, false);
      objectManipulator.applyProperties(parser, callbacks, false);

      const arraybuffer = payload.data.input;
      let executeFunctionName = 'execute';
      if (typeof parser.getParseFunctionName === 'function') executeFunctionName = parser.getParseFunctionName();
      if (payload.usesMeshDisassembler) {

        // TODO: Allow to plug and use generic MeshDisassembler

      } else {

        parser[executeFunctionName](arraybuffer, payload.data.options);

      }

      if (this.logging.enabled) console.log('WorkerRunner: Run complete!');

      self.postMessage({
        cmd: 'completeOverall',
        msg: 'WorkerRunner completed run.'
      });

    } else {

      console.error('WorkerRunner: Received unknown command: ' + payload.cmd);

    }

  }
};


/**
 * Default implementation of the WorkerRunner responsible for creation and configuration of the parser within the worker.
 * @constructor
 */
const WorkerRunner = function (payloadHandler) {

  this.payloadHandler = payloadHandler;

  const scope = this;
  const scopedRunner = function (event) {

    scope.processMessage(event.data);

  };

  self.addEventListener('message', scopedRunner, false);

};

WorkerRunner.prototype = {

  constructor: WorkerRunner,

  /**
   * Configures the Parser implementation according the supplied configuration object.
   *
   * @param {Object} payload Raw mesh description (buffers, params, materials) used to build one to many meshes.
   */
  processMessage: function (payload) {

    this.payloadHandler.handlePayload(payload);

  }

};

export {
  WorkerRunner,
  DefaultWorkerPayloadHandler,
  ObjectManipulator
};
